/*
 *    Copyright © OpenAtom Foundation.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.inspur.edp.cef.core.datatype;

import com.inspur.edp.cef.api.dataType.entity.ICefEntity;
import com.inspur.edp.cef.api.dataType.entity.ICefEntityContext;
import com.inspur.edp.cef.api.determination.DeterminationTypes;
import com.inspur.edp.cef.api.manager.ICefValueObjManager;
import com.inspur.edp.cef.api.validation.ValidationTypes;
import com.inspur.edp.cef.core.determination.EntityDeterminationExecutor;
import com.inspur.edp.cef.core.determination.builtinimpls.CefAfterModifyDtmAssembler;
import com.inspur.edp.cef.core.determination.builtinimpls.CefB4SaveDtmAssembler;
import com.inspur.edp.cef.core.determination.builtinimpls.CefRetrieveDefaultDtmAssembler;
import com.inspur.edp.cef.core.determination.fieldrtrimdtmadaptor.FieldRtrimDtmUtils;
import com.inspur.edp.cef.core.validation.builtinimpls.CefAfterModifyValAssembler;
import com.inspur.edp.cef.core.validation.builtinimpls.CefB4SaveValAssembler;
import com.inspur.edp.cef.core.validation.deletecheckadaptor.DeleteCheckValidationAdaptor;
import com.inspur.edp.cef.core.validation.enumvaladaptor.EnumValUtil;
import com.inspur.edp.cef.core.validation.lengthvaladaptor.LengthValUtils;
import com.inspur.edp.cef.core.validation.requiredvaladaptor.EntityValidationExecutor;
import com.inspur.edp.cef.core.validation.requiredvaladaptor.RequiredValsUtils;
import com.inspur.edp.cef.entity.entity.IEntityData;
import com.inspur.edp.cef.spi.common.UdtManagerUtil;
import com.inspur.edp.cef.spi.determination.IDetermination;
import com.inspur.edp.cef.spi.determination.IEntityRTDtmAssembler;
import com.inspur.edp.cef.spi.determination.basetypes.nestedTrans.CefNestedTransAfterModifyDtmAdapter;
import com.inspur.edp.cef.spi.determination.basetypes.nestedTrans.CefNestedTransB4SaveDtmAdapter;
import com.inspur.edp.cef.spi.determination.basetypes.nestedTrans.CefNestedTransRetrieveDefaultDtmAdapter;
import com.inspur.edp.cef.spi.entity.AssociationEnableState;
import com.inspur.edp.cef.spi.entity.AssociationInfo;
import com.inspur.edp.cef.spi.entity.info.propertyinfo.AssocationPropertyInfo;
import com.inspur.edp.cef.spi.entity.info.propertyinfo.DataTypePropertyInfo;
import com.inspur.edp.cef.spi.entity.info.propertyinfo.ObjectType;
import com.inspur.edp.cef.spi.entity.info.propertyinfo.SimpleAssoUdtPropertyInfo;
import com.inspur.edp.cef.spi.entity.info.propertyinfo.UdtPropertyInfo;
import com.inspur.edp.cef.spi.entity.resourceInfo.DataTypeResInfo;
import com.inspur.edp.cef.spi.entity.resourceInfo.builinImpls.CefEntityResInfoImpl;
import com.inspur.edp.cef.spi.extend.entity.ICefEntityExtend;
import com.inspur.edp.cef.spi.validation.IEntityRTValidationAssembler;
import com.inspur.edp.cef.spi.validation.IValidation;
import com.inspur.edp.cef.spi.validation.basetypes.nestedtrans.CefNestedTransAftModifyValAdapter;
import com.inspur.edp.cef.spi.validation.basetypes.nestedtrans.CefNestedTransB4SaveValAdapter;
import java.util.List;
import java.util.Map;
import lombok.SneakyThrows;

public abstract class CefEntity extends CefDataType implements ICefEntity
{
	public abstract String getID();

	public ICefEntityContext getEntityContext()
	{
		return (ICefEntityContext)super.getContext();
	}
	public void setContext(ICefEntityContext value)
	{super.setContext(value);}

	protected void checkEntityContext()
	{
		if (getContext() == null)
		{
			throw new UnsupportedOperationException("Invalid Context");
		}
	}
	public void retrieveDefaultDeterminate() {
		IEntityRTDtmAssembler ass =getRetrieveDefaultDtmAssembler();
		if (ass == null) {
			return;
		}

		if(extList != null){
			for(ICefEntityExtend ext:extList){
				ext.addExtDtm(ass, DeterminationTypes.RetrieveDefault);
			}
		}

		new EntityDeterminationExecutor(ass, getEntityContext()).execute();
	}

	private IEntityRTDtmAssembler getRetrieveDefaultDtmAssembler() {
		if(getVersion()==0)
		return createRetrieveDefaultDtmAssembler();
		if(getEntityCacheInfo().getRetrieveDefaultDtmAssembler()!=null)
			return getEntityCacheInfo().getRetrieveDefaultDtmAssembler();
		CefRetrieveDefaultDtmAssembler retrieveDefaultDtmAssembler = (CefRetrieveDefaultDtmAssembler) createRetrieveDefaultDtmAssembler();
		addRetrieveDefaultDtm(retrieveDefaultDtmAssembler);
		addBuiltInRetrieveDefaultDtms(retrieveDefaultDtmAssembler);
		getEntityCacheInfo().setRetrieveDefaultDtmAssembler(retrieveDefaultDtmAssembler);
		return retrieveDefaultDtmAssembler;
	}

	protected void addRetrieveDefaultDtm(CefRetrieveDefaultDtmAssembler assembler) {
	}

	private void addBuiltInRetrieveDefaultDtms(
			CefRetrieveDefaultDtmAssembler retrieveDefaultDtmAssembler) {
		addUdtTransRetrieveDefaultDtms(retrieveDefaultDtmAssembler);
		addChildTransRetrieveDefaultDtms(retrieveDefaultDtmAssembler);
	}

	protected void addChildTransRetrieveDefaultDtms(
			CefRetrieveDefaultDtmAssembler retrieveDefaultDtmAssembler) {

	}

	private void addUdtTransRetrieveDefaultDtms(
			CefRetrieveDefaultDtmAssembler retrieveDefaultDtmAssembler) {
		int index =0;
		for(Map.Entry entry:getEntityResInfo().getEntityTypeInfo().getPropertyInfos().entrySet())
		{
//			if(entry.getValue() instanceof UdtPropertyInfo==false)
			if(((DataTypePropertyInfo) entry.getValue()).getObjectInfo() instanceof UdtPropertyInfo==false)
				continue;
			DataTypePropertyInfo udtPropertyInfo= (DataTypePropertyInfo) entry.getValue();
			retrieveDefaultDtmAssembler.insertBelongingDetermination(createRetrieveDefaultUdtTransDtm(udtPropertyInfo),index++);
		}
	}

	private CefNestedTransRetrieveDefaultDtmAdapter createRetrieveDefaultUdtTransDtm(DataTypePropertyInfo udtPropertyInfo) {
		String udtConfigId = ((UdtPropertyInfo) udtPropertyInfo.getObjectInfo()).getUdtConfigId();
		ICefValueObjManager mgr = UdtManagerUtil.createManager(udtConfigId);
		CefNestedTransRetrieveDefaultDtmAdapter adapter =
				new CefNestedTransRetrieveDefaultDtmAdapter(udtPropertyInfo.getPropertyName(),
						udtPropertyInfo.getPropertyName(), udtConfigId, mgr.getTransmitType());
		return adapter;
	}

	protected IEntityRTDtmAssembler createRetrieveDefaultDtmAssembler()
	{
		return  null;
	}

	public void afterModifyDeterminate()
	{
		IEntityRTDtmAssembler ass = getAfterModifyDtmAssembler();
		if (ass == null)
		{
			return;
		}

		if(extList != null){
			for(ICefEntityExtend ext:extList){
				ext.addExtDtm(ass, DeterminationTypes.AfterModify);
			}
		}

		checkEntityContext();
		new EntityDeterminationExecutor(ass, getEntityContext()).execute();
	}

	protected final IEntityRTDtmAssembler getAfterModifyDtmAssembler() {
		if(getVersion()==0)
			return createAfterModifyDtmAssembler();
		if(getEntityCacheInfo().getAfterModifyDtmAssembler()!=null)
			return getEntityCacheInfo().getAfterModifyDtmAssembler();
		CefAfterModifyDtmAssembler afterModifyDtmAssembler = (CefAfterModifyDtmAssembler) createAfterModifyDtmAssembler();
		addAfterModifyDtm(afterModifyDtmAssembler);
		addBuiltInAfterModifyDtms(afterModifyDtmAssembler);
		getEntityCacheInfo().setAfterModifyDtmAssembler(afterModifyDtmAssembler);
		return afterModifyDtmAssembler;
	}

	protected void addAfterModifyDtm(CefAfterModifyDtmAssembler assembler) {
	}

	private void addBuiltInAfterModifyDtms(CefAfterModifyDtmAssembler afterModifyDtmAssembler) {
		addUdtTransAfterModifyDtms(afterModifyDtmAssembler);
		FieldRtrimDtmUtils.addFieldRtrimDtm(afterModifyDtmAssembler,getEntityResInfo().getEntityTypeInfo());
		addExtendAftModifyDtms(afterModifyDtmAssembler);
		addChildTransAfterModifyDtms(afterModifyDtmAssembler);
	}

	protected  void addExtendAftModifyDtms(CefAfterModifyDtmAssembler afterModifyDtmAssembler)
	{}

	protected void addChildTransAfterModifyDtms(CefAfterModifyDtmAssembler afterModifyDtmAssembler) {

	}

	private void addUdtTransAfterModifyDtms(CefAfterModifyDtmAssembler afterModifyDtmAssembler) {
		int index =0;
		for(Map.Entry entry:getEntityResInfo().getEntityTypeInfo().getPropertyInfos().entrySet())
		{
//			if(entry.getValue() instanceof UdtPropertyInfo==false)
			if(((DataTypePropertyInfo) entry.getValue()).getObjectInfo() instanceof UdtPropertyInfo==false)
				continue;
			DataTypePropertyInfo udtPropertyInfo= (DataTypePropertyInfo) entry.getValue();
			afterModifyDtmAssembler.insertBelongingDetermination(createAfterModifyUdtTransDtm(udtPropertyInfo),index++);
		}

	}

	private CefNestedTransAfterModifyDtmAdapter createAfterModifyUdtTransDtm(
			DataTypePropertyInfo udtPropertyInfo) {
		String udtConfigId = ((UdtPropertyInfo) udtPropertyInfo.getObjectInfo()).getUdtConfigId();
		ICefValueObjManager mgr = UdtManagerUtil.createManager(udtConfigId);
		CefNestedTransAfterModifyDtmAdapter adapter =
				new CefNestedTransAfterModifyDtmAdapter(udtPropertyInfo.getPropertyName(),
						udtPropertyInfo.getPropertyName(), udtConfigId, mgr.getTransmitType());
		return adapter;
	}

	protected IEntityRTDtmAssembler createAfterModifyDtmAssembler(){return null;}

	public void beforeSaveDeterminate()
	{

		IEntityRTDtmAssembler ass = getBeforeSaveDtmAssembler();
		if (ass == null)
		{
			return;
		}

		if(extList != null){
			for(ICefEntityExtend ext:extList){
				ext.addExtDtm(ass, DeterminationTypes.BeforeSave);
			}
		}

		checkEntityContext();
		new EntityDeterminationExecutor(ass, getEntityContext()).execute();
	}

	protected IEntityRTDtmAssembler createBeforeSaveDtmAssembler(){return null;}

	protected final IEntityRTDtmAssembler getBeforeSaveDtmAssembler() {
		if(getVersion()==0)
			return createBeforeSaveDtmAssembler();
		if(getEntityCacheInfo().getBeforeSaveDtmAssembler()!=null)
			return getEntityCacheInfo().getBeforeSaveDtmAssembler();
		CefB4SaveDtmAssembler b4SaveDtmAssembler = (CefB4SaveDtmAssembler) createBeforeSaveDtmAssembler();
		addBeforeSaveDtm(b4SaveDtmAssembler);
		addBuiltInB4SaveDtms(b4SaveDtmAssembler);
		getEntityCacheInfo().setBeforeSaveDtmAssembler(b4SaveDtmAssembler);
		return b4SaveDtmAssembler;
	}

	protected void addBeforeSaveDtm(CefB4SaveDtmAssembler assembler) {
	}

	private void addBuiltInB4SaveDtms(CefB4SaveDtmAssembler b4SaveDtmAssembler) {
		addUdtTransB4SaveDtms(b4SaveDtmAssembler);
		addChildTransB4SaveDtms(b4SaveDtmAssembler);
	}

  private void addUdtTransB4SaveDtms(CefB4SaveDtmAssembler b4SaveDtmAssembler) {
		int index =0;
		for(Map.Entry entry:getEntityResInfo().getEntityTypeInfo().getPropertyInfos().entrySet())
		{
//			if(entry.getValue() instanceof UdtPropertyInfo==false)
			if(((DataTypePropertyInfo) entry.getValue()).getObjectInfo() instanceof UdtPropertyInfo==false)
				continue;
			DataTypePropertyInfo udtPropertyInfo= (DataTypePropertyInfo) entry.getValue();
			b4SaveDtmAssembler.insertBelongingDetermination(createB4SaveUdtTransDtm(udtPropertyInfo),index++);
		}
	}

	private IDetermination createB4SaveUdtTransDtm(DataTypePropertyInfo udtPropertyInfo) {
		String udtConfigId = ((UdtPropertyInfo) udtPropertyInfo.getObjectInfo()).getUdtConfigId();
		ICefValueObjManager mgr = UdtManagerUtil.createManager(udtConfigId);
		return new CefNestedTransB4SaveDtmAdapter(udtPropertyInfo.getPropertyName(),udtPropertyInfo.getPropertyName(),
				udtConfigId, mgr.getTransmitType());
	}

	protected void addChildTransB4SaveDtms(CefB4SaveDtmAssembler b4SaveDtmAssembler) {

	}

	protected EntityDeterminationExecutor getDtmExecutor(IEntityRTDtmAssembler ass)
	{
		return new EntityDeterminationExecutor(ass, getEntityContext());
	}

	public void afterModifyValidate()
	{

		IEntityRTValidationAssembler ass = getAfterModifyValidationAssembler();
		if (ass == null)
		{
			return;
		}

		if(extList != null){
			for(ICefEntityExtend ext:extList){
				ext.addExtVal(ass, ValidationTypes.AfterModify);
			}
		}

		checkEntityContext();
		new EntityValidationExecutor(ass, getEntityContext()).execute();
	}

	private IEntityRTValidationAssembler getAfterModifyValidationAssembler() {
		if(getVersion()==0)
			return createAfterModifyValidationAssembler();
		if(getEntityCacheInfo().getAfterModifyValAssembler()!=null)
			return getEntityCacheInfo().getAfterModifyValAssembler();
		CefAfterModifyValAssembler afterModifyValAssembler = (CefAfterModifyValAssembler) createAfterModifyValidationAssembler();
		addAfterModifyVal(afterModifyValAssembler);
		addBuiltInAfterModifyVals(afterModifyValAssembler);
		getEntityCacheInfo().setAfterModifyValAssembler(afterModifyValAssembler);
		return afterModifyValAssembler;

	}

	protected void addAfterModifyVal(CefAfterModifyValAssembler assembler){
	}

	private void addBuiltInAfterModifyVals(CefAfterModifyValAssembler afterModifyValAssembler) {
		addUdtTransAfterModifyVals(afterModifyValAssembler);
		LengthValUtils.addLengthVals(afterModifyValAssembler,getEntityResInfo().getEntityTypeInfo(),getVersion());
		EnumValUtil.addEnumValUtil(afterModifyValAssembler,getEntityResInfo().getEntityTypeInfo());
		addChildTransAfterModifyVals(afterModifyValAssembler);
	}

	protected void addChildTransAfterModifyVals(CefAfterModifyValAssembler afterModifyValAssembler) {
	}

	private void addUdtTransAfterModifyVals(CefAfterModifyValAssembler afterModifyValAssembler) {
		int index =0;
		for(Map.Entry entry:getEntityResInfo().getEntityTypeInfo().getPropertyInfos().entrySet())
		{
			//if(entry.getValue() instanceof UdtPropertyInfo==false)
			if(((DataTypePropertyInfo) entry.getValue()).getObjectInfo() instanceof UdtPropertyInfo==false)
				continue;
			DataTypePropertyInfo udtPropertyInfo= (DataTypePropertyInfo) entry.getValue();
			afterModifyValAssembler.insertBelongingVals(createAfterModifyUdtTransVal(udtPropertyInfo),index++);
		}
	}

	private IValidation createAfterModifyUdtTransVal(DataTypePropertyInfo udtPropertyInfo) {
		return new CefNestedTransAftModifyValAdapter(udtPropertyInfo.getPropertyName(),udtPropertyInfo.getPropertyName(),((UdtPropertyInfo)udtPropertyInfo.getObjectInfo()).getUdtConfigId());
	}

	protected IEntityRTValidationAssembler createAfterModifyValidationAssembler(){return null;}

	public void beforeSaveValidate()
	{

		IEntityRTValidationAssembler ass = getBeforeSaveValidationAssembler();
		if (ass == null)
		{
			return;
		}

		if(extList != null){
			for(ICefEntityExtend ext:extList){
				ext.addExtVal(ass, ValidationTypes.BeforeSave);
			}
		}

		checkEntityContext();
		new EntityValidationExecutor(ass, getEntityContext()).execute();
	}

	private IEntityRTValidationAssembler getBeforeSaveValidationAssembler() {
		if(getVersion()==0)
			return createBeforeSaveValidationAssembler();
		if(getEntityCacheInfo().getBeforeSaveValAssembler()!=null)
			return getEntityCacheInfo().getBeforeSaveValAssembler();
		CefB4SaveValAssembler b4SaveValAssembler = (CefB4SaveValAssembler) createBeforeSaveValidationAssembler();
		addBeforeSaveVal(b4SaveValAssembler);
		addBuiltInB4SaveVals(b4SaveValAssembler);
		getEntityCacheInfo().setBeforeSaveValAssembler(b4SaveValAssembler);
		return b4SaveValAssembler;
	}

	protected void addBeforeSaveVal(CefB4SaveValAssembler assembler) {}

	private void addBuiltInB4SaveVals(CefB4SaveValAssembler b4SaveValAssembler) {
		addUdtTransB4SaveVals(b4SaveValAssembler);
		addFKConstraintValidations(b4SaveValAssembler);
		addUQConstraintValidations(b4SaveValAssembler);
		addSecretValidations(b4SaveValAssembler);
		addRequireValidations(b4SaveValAssembler);
		addExtendB4SaveVals(b4SaveValAssembler);
		addChildTransB4SaveVals(b4SaveValAssembler);
//		addDeleteCheckValidations(b4SaveValAssembler);
	}

  private void addRequireValidations(CefB4SaveValAssembler b4SaveValAssembler) {
		RequiredValsUtils.addRequiredVals(b4SaveValAssembler,getEntityResInfo().getEntityTypeInfo());
  }

  @SneakyThrows
  private void addFKConstraintValidations(CefB4SaveValAssembler b4SaveValAssembler) {
		for(DataTypePropertyInfo propertyInfo:getEntityResInfo().getEntityTypeInfo().getPropertyInfos().values())
		{
			AssociationInfo associationInfo=null;
			if(propertyInfo.getObjectType() == ObjectType.Association)
			{
				if(propertyInfo.getObjectInfo() instanceof AssocationPropertyInfo){
					associationInfo = ((AssocationPropertyInfo) propertyInfo.getObjectInfo()).getAssociationInfo();
				}
//				continue;
				//todo:待udtpropertyinfo完善了再写。
			}
			else if(propertyInfo.getObjectType()==ObjectType.UDT && propertyInfo.getObjectInfo() instanceof SimpleAssoUdtPropertyInfo)
			{
				associationInfo = ((SimpleAssoUdtPropertyInfo) propertyInfo.getObjectInfo()).getAssoInfo().getAssociationInfo();
			}

			if(associationInfo==null || associationInfo.getEnableState() == AssociationEnableState.Disabled)
				continue;
			Class c=Class.forName("com.inspur.edp.bef.builtincomponents.FKConstraints.FKCheckValidationAdaptor");
		  b4SaveValAssembler.addValidations(
					(IValidation) c.getConstructor(String.class,String.class,String.class,String.class,String.class).newInstance(propertyInfo.getPropertyName(),associationInfo.getConfig(),associationInfo.getNodeCode(),associationInfo.getMainCode(),associationInfo.getPrivateSourceColumn()));
		}
  }

	@SneakyThrows
	private void addUQConstraintValidations(CefB4SaveValAssembler b4SaveValAssembler) {
		Class c=Class.forName("com.inspur.edp.bef.builtincomponents.UQConstraints.BeforeSaveUQConstraintValidation");
		b4SaveValAssembler.addValidations((IValidation) c.getConstructor(CefEntityResInfoImpl.class).newInstance(getEntityResInfo()));
	}

	@SneakyThrows
	private void addSecretValidations(CefB4SaveValAssembler b4SaveValAssembler) {
		Class c=Class.forName("com.inspur.edp.bef.builtincomponents.SecretLevel.SecretValidation");
		b4SaveValAssembler.addValidations((IValidation) c.getConstructor().newInstance());
	}

//	@SneakyThrows
//	private void addDeleteCheckValidations(CefB4SaveValAssembler b4SaveValAssembler) {
//		b4SaveValAssembler.addValidations(new DeleteCheckValidationAdaptor(this.getEntityResInfo().getConfigId(), this.getEntityResInfo().isRoot() ?"RootNode" : this.getEntityResInfo().getEntityCode()));
//	}

  protected void addExtendB4SaveVals(CefB4SaveValAssembler b4SaveValAssembler) {

	}

	protected void addChildTransB4SaveVals(CefB4SaveValAssembler b4SaveValAssembler) {

	}

	private void addUdtTransB4SaveVals(CefB4SaveValAssembler b4SaveValAssembler) {
		int index =0;
		for(Map.Entry entry:getEntityResInfo().getEntityTypeInfo().getPropertyInfos().entrySet())
		{
			if(((DataTypePropertyInfo) entry.getValue()).getObjectInfo() instanceof UdtPropertyInfo==false)
				continue;
			DataTypePropertyInfo udtPropertyInfo= (DataTypePropertyInfo) entry.getValue();
			b4SaveValAssembler.insertBelongingVals(createB4SaveUdtTransVal(udtPropertyInfo),index++);
		}
	}

	private IValidation createB4SaveUdtTransVal(DataTypePropertyInfo udtPropertyInfo) {
		return new CefNestedTransB4SaveValAdapter(udtPropertyInfo.getPropertyName(),udtPropertyInfo.getPropertyName(),((UdtPropertyInfo)udtPropertyInfo.getObjectInfo()).getUdtConfigId(), udtPropertyInfo);
	}

	protected IEntityRTValidationAssembler createBeforeSaveValidationAssembler() {return null;}

	public abstract ICefEntity getChildEntity(String code, String id);


	protected List<ICefEntityExtend> extList;

	public final void setExtend(List<ICefEntityExtend> extList) {
		this.extList = extList;
	}


	@Override
	public final DataTypeResInfo getDataTypeResInfo() {
		return getEntityResInfo();
	}

	public CefEntityResInfoImpl getEntityResInfo()
	{return null;}

	@Override
	protected final CefDataTypeCacheInfo getDataTypeCacheInfo() {
		return getEntityCacheInfo();
	}

	protected CefEntityCacheInfo getEntityCacheInfo() {
		throw new RuntimeException();
	}

	@Override
	public void assignDefaultValue(IEntityData data) {
		if(getEntityResInfo() == null)
			return;
		for(Map.Entry<String, DataTypePropertyInfo> entry:getEntityResInfo().getEntityTypeInfo().getPropertyInfos().entrySet()){
			if(entry.getValue().getDefaultValue() != null){
				switch (entry.getValue().getDefaultValue().getValueType()){
					case Value:
						data.setValue(entry.getValue().getPropertyName(), entry.getValue().getDefaultValue().getRealValue());
						break;
					case Expression:
						data.setValue((entry.getValue()).getPropertyName(), entry.getValue().getDefaultValue().getRealValue());
						break;
				}
			}
		}
	}

}
